// draws to the screen

#include "GL/glut.h"
#include <sstream>
#include <sys/stat.h>

#include "draw.h"
#include "board.h"
#include "soundManager.h"

bool showTitle = false;
bool showEnding = false;
bool showCutScene = false;
bool showPause = false;
bool showGameOver = false;

CutScene cutScene = LEVEL_0;

void initGL() {
	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_LIGHTING);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
}

// i and j are in tile coordinates, (0, 0) is the upper left tile
static
void drawTile(TILE_TYPE tile, int board_w, int board_h, int i, int j) {
	static const float screen_w = 2.0f;	// in screen coords
	static const float screen_h = 2.0f;	// in screen coords

	static const float screen_x_base = -1.0f;	// we draw left to right
	static const float screen_y_base =  1.0f;	// we draw top to bottom

	static const float x_off = 16.0f;		// in pixels, outer border on one side in width
	static const float y_off = 12.0f;		// in pixels, outer border on one side in height

	static const float screen_x_off = (x_off * screen_w) / WINDOW_WIDTH;
	//static const float screen_y_off = (y_off * screen_h) / WINDOW_HEIGHT;

	// total width is 2.0f, total height is 2.0f
	float screen_step_x = (screen_w - 2.0f*screen_x_off) / (float)board_w;
	//float screen_step_y = (screen_h - 2.0f*screen_y_off) / (float)board_h;

	float screen_x = screen_x_base + screen_x_off + i*screen_step_x;
	// subtract one step in y because we need to go to the
	// bottom left corner of the current tile
	//float screen_y = screen_y_base - (screen_y_off + screen_step_y) - j*screen_step_y;

	float draw_h = screen_h - (screen_h * ((2 * y_off) / WINDOW_HEIGHT));
	float screen_y = screen_y_base - ((float)(j + 1) * (draw_h / (float)board_h));

	glRasterPos2f(screen_x, screen_y);

	if ((tile >= FLOOR) && (tile <= EXIT))
	{
		Bitmap &bmp = *getTileBmp(tile);
		assert(bmp.bpp == 24 || bmp.bpp == 32);
		glDisable(GL_BLEND);

		if (bmp.bpp == 24) {
			glDrawPixels(bmp.width, bmp.height, GL_RGB, GL_UNSIGNED_BYTE, bmp.data);
		} else {
			glDrawPixels(bmp.width, bmp.height, GL_RGBA, GL_UNSIGNED_BYTE, bmp.data);
		}
	}
	else
	{
		Png &png = *getTilePng(tile);
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glDrawPixels(png.width, png.height, GL_RGBA, GL_UNSIGNED_BYTE, png.data);
	}
}

static
void drawBoardBackground() {
	Board &b = *getCurrentBoard();
	int board_w = b.getWidth();
	int board_h = b.getHeight();

	for (int i = 0; i < board_w; i++) {
		for (int j = 0; j < board_h; j++) {
			drawTile(b.getTile(i, j), board_w, board_h, i, j);
		}
	}
}

static
void drawActors() {
	Board &b = *getCurrentBoard();
	int board_w = b.getWidth();
	int board_h = b.getHeight();

	size_t n;

	// draw illusions before any enemeies
	vector<Food> &foods = b.getFoods();
	n = foods.size();
	for (size_t i = 0; i < n; i++) {
		Food f = foods[i];
		drawTile(f.getTile(), board_w, board_h, f.x, f.y);
	}

	vector<DeadBody> &deadBodies = b.getDeadBodies();
	n = deadBodies.size();
	for (size_t i = 0; i < n; i++) {
		DeadBody d = deadBodies[i];
		drawTile(d.getTile(), board_w, board_h, d.x, d.y);
	}

	// draw enemies
	vector<Guard> &guards = b.getGuards();
	n = guards.size();
	for (size_t i = 0; i < n; i++) {
		Guard g = guards[i];
		drawTile(g.getTile(), board_w, board_h, g.x, g.y);
	}

	vector<Dog> &dogs = b.getDogs();
	n = dogs.size();
	for (size_t i = 0; i < n; i++) {
		Dog d = dogs[i];
		drawTile(d.getTile(), board_w, board_h, d.x, d.y);
	}

	vector<SG> &sgs = b.getSgs();
	n = sgs.size();
	for (size_t i = 0; i < n; i++) {
		SG s = sgs[i];
		drawTile(s.getTile(), board_w, board_h, s.x, s.y);
	}

	// draw illusionist last
	Ill &ill = b.getIll();
	drawTile(ill.getTile(), board_w, board_h, ill.x, ill.y);
}

string convertInt(int number, int places)
{
	stringstream ss;//create a stringstream
	ss << number;//add number to the stream

	int filler = places - ss.str().size();
	string fill;

	for (int i = 0; i < filler; i++)
		fill.push_back('0');

	return fill + ss.str();
}

void glutBitmapString(void *font, const string str)
{
	GLfloat rasterPos[4];

	glGetFloatv(GL_CURRENT_RASTER_POSITION, rasterPos);
	// Descale from window coordinates
	rasterPos[0] = -1.0f + (2.0f * (rasterPos[0] / (float)WINDOW_WIDTH));
	rasterPos[1] = -1.0f + (2.0f * (rasterPos[1] / (float)WINDOW_HEIGHT));

	for  (int i = 0, end = str.length(); i < end; i++)
	{
		glRasterPos2f(rasterPos[0], rasterPos[1]);
		glutBitmapCharacter(font, str[i]);
		rasterPos[0] += 2.0f * ((float)glutBitmapWidth(font, str[i]) / WINDOW_WIDTH);
	}
}

static
void drawHud()
{
	Board &b = *getCurrentBoard();

	glColor3f(255.0, 0.0, 0.0);
	glRasterPos2f(-0.96, -0.98);
	glutBitmapString(GLUT_BITMAP_9_BY_15, "Lives x ");
	glColor3f(0.0, 255.0, 0.0);
	glutBitmapString(GLUT_BITMAP_9_BY_15, convertInt(b.getIllLives(), 2));
	glColor3f(0.0, 255.0, 255.0);
	glutBitmapString(GLUT_BITMAP_9_BY_15, " || ");
	glColor3f(255.0, 255.0, 0.0);
	glutBitmapString(GLUT_BITMAP_9_BY_15, "Food [H]: ");
	glColor3f(0.0, 255.0, 0.0);
	glutBitmapString(GLUT_BITMAP_9_BY_15, convertInt(b.getFoodUse(), 2));
	glColor3f(0.0, 255.0, 255.0);
	glutBitmapString(GLUT_BITMAP_9_BY_15, " || ");
	glColor3f(255.0, 255.0, 0.0);
	glutBitmapString(GLUT_BITMAP_9_BY_15, "Dead Body [J]: ");
	glColor3f(0.0, 255.0, 0.0);
	glutBitmapString(GLUT_BITMAP_9_BY_15, convertInt(b.getDeadBodyUse(), 2));
	glColor3f(0.0, 255.0, 255.0);
	glutBitmapString(GLUT_BITMAP_9_BY_15, " || ");
	glColor3f(255.0, 255.0, 0.0);
	glutBitmapString(GLUT_BITMAP_9_BY_15, "Cloak [K]: ");
	glColor3f(0.0, 255.0, 0.0);
	glutBitmapString(GLUT_BITMAP_9_BY_15, convertInt(b.getCloakUse(), 2));
	glColor3f(0.0, 255.0, 255.0);
	glutBitmapString(GLUT_BITMAP_9_BY_15, " || ");
	glColor3f(255.0, 255.0, 0.0);
	glutBitmapString(GLUT_BITMAP_9_BY_15, "Impostor [L]: ");
	glColor3f(0.0, 255.0, 0.0);
	glutBitmapString(GLUT_BITMAP_9_BY_15, convertInt(b.getFakeUse(), 2));
}

static
void drawBoard() {
	drawBoardBackground();
	drawActors();
	drawHud();
}

void drawTitle() {
	Bitmap &bmp = *getTitleScreen();

	glRasterPos2f(-1, -1);
	assert(bmp.bpp == 24 || bmp.bpp == 32);
	if (bmp.bpp == 24) {
		glDrawPixels(bmp.width, bmp.height, GL_RGB, GL_UNSIGNED_BYTE, bmp.data);
	} else {
		glDrawPixels(bmp.width, bmp.height, GL_RGBA, GL_UNSIGNED_BYTE, bmp.data);
	}
}

/**
 * This function will draw the current cutscene.  If the file does not exist,
 * then it will return false.  There can be up to ten cutscenes before a level.
 * example - Level 0 : cutscene000.bmp ---> cutscene009.bmp
 *           Level 11: cutscene100.bmp ---> cutscene109.bmp
 * Ending cutscenes will start at the level number after the last level.
 */
bool drawCutScene() {
	string filename = getCutSceneRoot() + convertInt(cutScene, 3) + ".bmp";
	struct stat stFileInfo;

	if (stat(filename.data(), &stFileInfo) != 0)
	{
		// End of this level's cutscenes, increment to the start of the next level.
		cutScene = (CutScene)(((int)cutScene + 10) - ((int)cutScene % 10));
		return false;
	}
	Bitmap &bmp = *createCutScene(convertInt(cutScene, 3) + ".bmp");

	glRasterPos2f(-1, -1);
	assert(bmp.bpp == 24 || bmp.bpp == 32);
	if (bmp.bpp == 24) {
		glDrawPixels(bmp.width, bmp.height, GL_RGB, GL_UNSIGNED_BYTE, bmp.data);
	} else {
		glDrawPixels(bmp.width, bmp.height, GL_RGBA, GL_UNSIGNED_BYTE, bmp.data);
	}
	return true;
}

void drawPause()
{
	Png &png = *getPauseScreen();
	glRasterPos2f(-1, -1);
	glEnable(GL_BLEND);
	glBlendFunc(GL_ONE, GL_DST_ALPHA);
	glDrawPixels(png.width, png.height, GL_RGBA, GL_UNSIGNED_BYTE, png.data);
}

void drawGameOver()
{
	Bitmap &bmp = *getGameOverScreen();

	glRasterPos2f(-1, -1);
	assert(bmp.bpp == 24 || bmp.bpp == 32);
	if (bmp.bpp == 24) {
		glDrawPixels(bmp.width, bmp.height, GL_RGB, GL_UNSIGNED_BYTE, bmp.data);
	} else {
		glDrawPixels(bmp.width, bmp.height, GL_RGBA, GL_UNSIGNED_BYTE, bmp.data);
	}
}

void drawEnding() {
	Bitmap &bmp = *getEndingScreen();

	glRasterPos2f(-1, -1);
	assert(bmp.bpp == 24 || bmp.bpp == 32);
	if (bmp.bpp == 24) {
		glDrawPixels(bmp.width, bmp.height, GL_RGB, GL_UNSIGNED_BYTE, bmp.data);
	} else {
		glDrawPixels(bmp.width, bmp.height, GL_RGBA, GL_UNSIGNED_BYTE, bmp.data);
	}
}

void draw() {
	if (showTitle) {
		drawTitle();
		glutSwapBuffers();
		return;
	}

	if (showCutScene && !showEnding)
	{
		showCutScene = drawCutScene();
		if (!showCutScene) gotoMainGame();
		glutSwapBuffers();
		return;
	}

	if (showGameOver)
	{
		drawGameOver();
		clearCutScenes();
		glutSwapBuffers();
		return;
	}

	Board *b = getCurrentBoard();

	if (showEnding || b == NULL) {
		if (showCutScene)
		{
			showCutScene = drawCutScene();
		}
		else
		{
			drawEnding();
			clearCutScenes();
		}
		glutSwapBuffers();
		return;
	}

	if (b->hasLost()) {
		if (b->getIllLives() >= 0)
		{
			loadCurrBoard(b->getIllLives());
		}
		else
		{
			loadInitialBoard();
			gotoGameOver();
		}

		// we've reset the board, grab the new instance
		//b = getCurrentBoard();
		glutSwapBuffers();
		return;
	}

	if (b->hasWon()) {
		playSfx(SFX_LEVEL_VICTORY);
		loadNextBoard();

		// we've reset the board, grab the new instance
		b = getCurrentBoard();

		if (b == NULL) {
			loadInitialBoard();
			gotoEnding();
		}
		else
		{
			gotoCutScene();
		}
	}

	glClear(GL_COLOR_BUFFER_BIT);

	drawBoard();
	if (showPause) drawPause();

	glutSwapBuffers();
}

void gotoTitle() {
	cutScene = LEVEL_0;
	showCutScene = false;
	showPause = false;
	showGameOver = false;
	showEnding = false;
	showTitle = true;
	playBgm(BGM_TITLE);
}

void gotoMainGame() {
	showTitle = false;
	showCutScene = false;
	showPause = false;
	showGameOver = false;
	showEnding = false;
	playBgm(BGM_STAGE);
}

void gotoCutScene()
{
	showTitle = false;
	showPause = false;
	showGameOver = false;
	showEnding = false;
	showCutScene = true;
	playBgm(BGM_CUTSCENE);
}

void incrementCutScene()
{
	cutScene = (CutScene)((int)cutScene + 1);
}

void togglePause()
{
	if (showPause) showPause = false;
	else showPause = true;
}

void gotoGameOver()
{
	showTitle = false;
	showPause = false;
	showCutScene = false;
	showEnding = false;
	showGameOver = true;
	playBgm(BGM_NONE);
	playSfx(SFX_GAME_OVER);
}

void gotoEnding() {
	showTitle = false;
	showPause = false;
	showCutScene = true;
	showEnding = true;
	playBgm(BGM_ENDING);
}

bool atTitle() {
	return showTitle;
}

bool atCutScene()
{
	return showCutScene;
}

bool atPause()
{
	return showPause;
}

bool atGameOver()
{
	return showGameOver;
}

bool atEnding() {
	return showEnding;
}

// end of draw.cpp
